if (!window.org) var org = {};
if (!org.dreamwork) org.dreamwork = {};
if (!org.dreamwork.dandelion) org.dreamwork.dandelion = {};

// dataset
org.dreamwork.dandelion.DataSet = function (name) {
    var datasetName = name;
    var tables = new org.dreamwork.dandelion.Collection ();
    var relations = new org.dreamwork.dandelion.Collection ();

	this.getDataSetName = function () { return datasetName; };
    this.setDataSetName = function (name) { datasetName = name; }
    this.addTable = function (table) { tables.addElement (table); }
	this.getTable = function (name) { return tables.getElement (name); }
	this.getTableCount = function () { return tables.getLength(); }

    this.toString = function () { return "org.dreamwork.dandelion.Dataset[Name=" + name + ",TableCount=" + tables.getLength () + "]"; }

	this.toXML = function ()
	{
        var s = "<?xml version = '1.0' encoding = 'UTF-8'?>\n";
        s += "<dataset name = '" + name + "'>\n";
        for (var i = 0; i < dataset.getTableCount(); i ++) {
            var table = this.getTable(i);
            for (var j = 0; j < table.getRowCount(); j ++) {
				s += "\t<" + table.getTableName() + ">\n";
                var row = table.getRows () [j];
                for (var k = 0; k < table.getColumnCount(); k ++) {
                    var column = table.getColumn (k);
                    s += "\t\t<" + column.getColumnName() + ">" + row.getValue (k) + "</" + column.getColumnName() + ">\n";
                }
				s += "\t</" + table.getTableName() + ">\n";
            }
        }
		s += "</dataset>";

        return s;
	}
}

// datatable
org.dreamwork.dandelion.DataTable = function (name) {
	var columns = new org.dreamwork.dandelion.Collection ();
    var rows = [];
    var deletedRows = [];

    this.getName = function () { return this.getTableName(); }
    this.getTableName = function () { return name; }
    this.addColumn = function (column) { columns.addElement (column); }
    this.getColumnCount = function () { return columns.getLength(); }
    this.getColumn = function (index) { return columns.getElement (index); }
    this.removeColumn = function (index) { columns.removeElement (index); }
    this.getColumns = function () { return columns; }
    this.getRowCount = function () { return rows.length; }
    this.getRows = function () { return rows; }

    this.__deleteRow__ = function (row) {
        var index = rows.indexOf (row);
        rows.removeElement (row);
        deletedRows ["" + index] = row;
    }

    this.createRow = function () {
        var row = new org.dreamwork.dandelion.DataRow (this);
        row.data.length = columns.getLength();
        row.oldData.length = columns.getLength();
        return row;
    }

    this.addRow = function (row) {
        rows.push (row);
        row.__setStatus__(org.dreamwork.dandelion.DataRow.STATE_ADDED);
    }

    this.acceptChange = function () {
        for (var i = 0; i < rows.length; i ++) rows [i].acceptChange ();
        deletedRows = [];
    }

    this.rejectChange = function () {
        for (var i = 0; i < rows.length; i ++) rows [i].rejectChange ();
        for (var i = 0; i < deletedRows.length; i ++) rows.push (deletedRows [i]);
        deletedRows = [];
    }

	this.toString = function () {
		var s = name + "->{\n";
		for (var i = 0; i < columns.getLength (); i ++)
		{
			var column = columns.getElement (i);
			s += "\t" + column.getColumnName () + "[" + column.getDataType () + "]\n";
		}
		s += "}";
		return s;
	}

	this.clear = function ()
	{
		rows = [];
		deletedRows = [];
	}

    this.findBy = function (column, value) {
        if (rows.length == 0) return null;
        for (var i = 0; i < rows.length; i ++)
            if (rows [i].getValue(column) == value) return rows [i];
        return null;
    }
}

// datacolumn
org.dreamwork.dandelion.DataColumn = function (name) {
	var table;
	var type;
    this.getName = function () { return this.getColumnName(); }    
    this.getColumnName = function () { return name; }
	this.setDataTable = function (newTable) { 
		table = newTable; 
		table.addColumn (this);
	}
	this.getDataTable = function () { return table; }
	this.setDataType = function (newType) { type = newType; }
	this.getDataType = function () { return type; }
}

// datarow
org.dreamwork.dandelion.DataRow = function (table)
{
    this.data = [];
    this.oldData = [];
    var rowState = org.dreamwork.dandelion.DataRow.STATE_DETACHED;
    var columns = table.getColumns ();

    this.setValue = function (index, value) {
        var pos = -1;
        if ((index instanceof org.dreamwork.dandelion.DataColumn) || (typeof index == "string"))
            pos = columns.indexOf (index);
        else if (typeof index == "number")
            pos = index;
        if (pos != -1) {
			this.data [pos] = value;
			this.__setStatus__ (org.dreamwork.dandelion.DataRow.STATE_MODIFIED);
		} else
			throw "can't find column: " + index;
    }

    this.getValue = function (index)
    {
        pos = -1;
        if ((index instanceof org.dreamwork.dandelion.DataColumn) || (typeof index == "string"))
            pos = columns.indexOf (index);
        else if (typeof index == "number")
            pos = index;
        if (pos != -1) return this.data [pos];
		throw "can't find column: " + index;
    }

    this.deleteRow = function ()
    {
        rowState = org.dreamwork.dandelion.DataRow.STATE_DELETED;
        table.__deleteRow__ (this);
    }

    this.acceptChange = function ()
    {
        if (rowState == org.dreamwork.dandelion.DataRow.STATE_DETACHED)
            throw "Can't accept changes in STATE_DETACHED status.";

        if (rowState != org.dreamwork.dandelion.DataRow.STATE_UNCHANGED)
        {
            for (var i = 0; i < this.oldData.length; i ++)
                this.oldData [i] = this.data [i];
            rowState = org.dreamwork.dandelion.DataRow.STATE_UNCHANGED;
        }
    }

    this.rejectChange = function ()
    {
        if (rowState == org.dreamwork.dandelion.DataRow.STATE_DETACHED)
            throw "Can't reject changes in STATE_DETACHED status.";

        if (rowState != org.dreamwork.dandelion.DataRow.STATE_UNCHANGED)
        {
            for (var i = 0; i < this.data.length; i ++)
                this.data [i] = this.oldData [i];
            rowState = org.dreamwork.dandelion.DataRow.STATE_UNCHANGED;
        }
    }

    this.getTable = function () { return table; }

    this.__setStatus__ = function (status) { rowState = status; }
}

org.dreamwork.dandelion.DataRow.STATE_ADDED = 1;
org.dreamwork.dandelion.DataRow.STATE_DELETED = 2;
org.dreamwork.dandelion.DataRow.STATE_DETACHED = 3;
org.dreamwork.dandelion.DataRow.STATE_MODIFIED = 4;
org.dreamwork.dandelion.DataRow.STATE_UNCHANGED = 5;

// collection
org.dreamwork.dandelion.Collection = function ()
{
    var array_by_index = [];
    var array_by_name = [];
    this.test = array_by_index;
    this.a2 = array_by_name;

    this.getLength = function () { return array_by_index.length; }

    this.addElement = function (obj)
    {
        var objName = obj.getName().toUpperCase();
        var name = array_by_name.containsKey (objName);
        if (!name) array_by_index.push (objName);
        array_by_name [objName] = obj;
    }

    this.getElement = function (index)
    {
        if (typeof index == "number") return array_by_name [array_by_index [index]];
        if (typeof index == "string") return array_by_name [index.toUpperCase ()];
        return null;
    }

    this.removeElement = function (index)
    {
        if (typeof index == "number")
        {
            if (index >= 0 && index < array_by_index.length)
            {
                var name = array_by_index [index];
                array_by_index.removeElement (index);
                array_by_name.removeElement (name);
            }
        }
        else if (typeof index == "string")
        {
            var name = index.toUpperCase();
            array_by_index.removeElement(name);
            array_by_name.removeElement(name);
        }
        else
        {
            var name = index.getName();
            array_by_index.removeElement(name);
            array_by_name.removeElement(name);
        }
    }

    this.indexOf = function (obj)
    {
        if (typeof obj == 'string') return array_by_index.indexOf (obj.toUpperCase());
        var name = obj.getName().toUpperCase();
        return array_by_index.indexOf(name);
    }
}

org.dreamwork.dandelion.XMLAdapter = function (xml)
{
	var doc = buildDoc (xml);

    // public methods
    this.fill = function (dataset, tableName)
    {
        var root = doc.firstChild;
        while (root != null && root.nodeType != 1) root = root.nextSibling;
        if (root == null) return;
        dataset.setDataSetName (root.getAttribute("name"));
        var children = root.getElementsByTagName(tableName);
        if (children == null || children.length == 0) return;
        var table = dataset.getTable (tableName);
        if (table == null)
        {
            table = new org.dreamwork.dandelion.DataTable (tableName);
            dataset.addTable (table);
        }

        var start = 0, size = Number.MAX_VALUE;
        if (arguments.length >= 3) start = arguments [2];
        if (arguments.length >= 4) size = arguments [3];

		var tableShown = false;
        for (var i = start; i < start + size && i < children.length; i ++)
        {
            var e = children [i];
            var columnNodes = e.childNodes;
            for (var j = 0; j < columnNodes.length; j ++)
            {
                var node = columnNodes [j];
                // text node
                if (node.nodeType == 3) continue;

                var columnName = node.nodeName;
                var column = table.getColumn (columnName);
                if (column == null)
                {
                    column = new org.dreamwork.dandelion.DataColumn (columnName);
                    table.addColumn (column);
                }
            }

            var row = table.createRow ();
            for (var j = 0; j < columnNodes.length; j ++)
            {
                var node = columnNodes [j];
				if (node.nodeType == 3) continue;
                var columnName = node.nodeName;
                var column = table.getColumn(columnName);
                if (node.firstChild == null) 
				{
					row.setValue (columnName, null);
				} else if (node.firstChild.nodeType == 3) // text-node
                {
                    var value = node.firstChild.nodeValue.trim ();
                    if (Number.isInteger(value))
                    {
                        column.setDataType ("int");
                        row.setValue(columnName, parseInt (value, 10));
                    }
                    else if (Number.isFloat(value))
                    {
                        column.setDataType ("double");
                        row.setValue(columnName, parseFloat (value, 10));
                    }
                    else if (Date.isDateString (value))
                    {
                        column.setDataType ("date");
                        row.setValue (columnName, Date.parseDate(value));
                    }
                    else if (Date.isTimeString(value))
                    {
                        column.setDataType ("datetime");
                        row.setValue (columnName, Date.parseTime(value));
                    }
                    else
                    {
                        column.setDataType ("varchar");
                        row.setValue (columnName, value);
                    }
                }
            }
            table.addRow (row);
        }
        table.acceptChange ();
    }

    this.save = function (dataset) {
        var s = "<?xml version = '1.0' encoding = 'UTF-8'?>\n";
        s += "<dataset name = '" + dataset.getDataSetName() + "'>\n";
        for (var i = 0; i < dataset.getTableCount(); i ++) {
            var table = dataset.getTable(i);
            for (var j = 0; j < table.getRowCount(); j ++) {
				s += "\t<" + table.getTableName() + ">\n";
                var row = table.getRows () [j];
                for (var k = 0; k < table.getColumnCount(); k ++) {
                    var column = table.getColumn (k);
                    s += "\t\t<" + column.getColumnName() + ">" + row.getValue (k) + "</" + column.getColumnName() + ">\n";
                }
				s += "\t</" + table.getTableName() + ">\n";
            }
        }
		s += "</dataset>";

        return s;
    }

    // private methods
    function buildDoc (xml)
    {
        var d = DOMDelegate.createDocument ();
        d.loadXML (xml);
        return d;
    }
}